use std::fmt::Debug;

/// 高阶生命周期
/// 闭包可以作为函数的参数和返回值，那么闭包参数中如果含有引用的话，其生命周期参数该如何标注?
pub fn start() {
    let x = Box::new(&2usize);
    bar(x);

    let elm = Pick {
        data: (3, 4),
        func: max,
    };
    println!("{}", elm.call());
}

trait DoSomething<T> {
    fn do_sth(&self, value: T);
}

impl<'a, T: Debug> DoSomething<T> for &'a usize {
    fn do_sth(&self, value: T) {
        println!("{:?}", value);
    }
}

// Rust为此专门提供了一个方案， 叫作高阶生命周期 (Higher-Ranked Lifetime)，
// 也叫高阶 trait 限定 (Higher-Ranked Trait Bound, HRTB)
// lifetime `'a` defined here
// fn foo<'a>(b: Box<dyn DoSomething<&'a usize>>) {
//     // 这里的生命周期并不能大于 'a，但是 DoSomething是<&'a usize>
//     // 编译会出错
//     // borrowed value does not live long enough
//     //  argument requires that `s` is borrowed for `'a`
//     let s : usize = 10;
//     b.do_sth(&s);
// }
// 修改为
// for<>语法整体表示此生命周期参数只针对其后面所跟着的“对象”，
// 在本例中是 DoSomething<&’f usize>，生命周期参数’f是在 for<’f>中声明的。
// 使用 for<’f>语法，就代 表 bar 函数的生命周期和 DoSomething<&’f usize>没有直接关系,所以编译正常
fn bar(b: Box<dyn for<'f> DoSomething<&'f usize>>) {
    let s: usize = 10;
    b.do_sth(&s);
}

// 实际开发中会经常用闭包，而闭包实现的三个 trait本身也是泛型 trait，所以肯定也存在
// 闭包参数和返回值都是引用类型的情况，
struct Pick<F> {
    data: (u32, u32),
    func: F,
}

// F: Fn(&(u32, u32)) -> &u32, 此处的 trait限定中使用了引用类型，
// 但是并没有显式地标记生命周期参数，为什么可以正常编译呢?这是因为编译器自动为其补齐了生命周期参数。
// impl<F> Pick<F>
//     where F: Fn(&(u32, u32)) -> &u32 {
//
//     fn call(&self) -> &u32 {
//         (self.func)(&self.data)
//     }
// }

// 下面是显示指定
impl<F> Pick<F>
where
    F: for<'f> Fn(&'f (u32, u32)) -> &'f u32,
{
    fn call(&self) -> &u32 {
        (self.func)(&self.data)
    }
}

fn max(data: &(u32, u32)) -> &u32 {
    if data.0 > data.1 {
        &data.0
    } else {
        &data.1
    }
}
